package com.bawan.vims.common.helper;

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bawan.vims.common.ResponseModel;

/**
 * HTTP请求处理工具类
 * <p> Description	: HttpClientHelper.java </p>
 * <p> Copyright	: Copyright (c) 2015 by FITT</p>
 * <p> Author 		: ZCC </p>
 * <p> Create		: 2015年1月29日 下午5:27:09 </p> 
 * <p> version 		: 1.0 </p>
 */
public class HttpClientHelper {
    private static Logger log = LoggerFactory.getLogger(HttpClientHelper.class);

    /**
     * HttpClient实例缓存
     */
    private static LinkedList<HttpClient> HttpClient_Cache = new LinkedList<HttpClient>();

    private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 私有化构造函数,实现单例
     */
    private HttpClientHelper(){}

    private static final String charset_utf8 = "UTF-8";
    
    /**
     * 定义私有静态类,内部定义一个私有静态常量实例
     */
    private static class HttpClientHelperHolder {
    	private static final HttpClientHelper INSTANCE = new HttpClientHelper();
    }
    /**
     * 获得 HttpClientHelper 实例
     * @return
     */
    public static HttpClientHelper getInstance() {
        return HttpClientHelperHolder.INSTANCE;
    }

    /**
     * 以FORM形式实现HTTP POST请求
     * @param postUrl
     * @param params
     * @return
     */
	@SuppressWarnings("rawtypes")
	public ResponseModel post(String postUrl, Map params) {
        PostMethod postMethod = newPostMethod(postUrl, params);
        return request(postMethod);
    }
    
	/**
	 * 以JSON形式发送HTTP POST请求
	 * @param postUrl
	 * @param paramJson
	 * @return
	 */
	public ResponseModel post(String postUrl, String paramJson) {
        try {
			StringRequestEntity requestEntity = new StringRequestEntity(paramJson,"application/json","UTF-8");
			PostMethod postMethod = new PostMethod(postUrl);
			postMethod.setRequestEntity(requestEntity);
			
			return request(postMethod);
		} catch (UnsupportedEncodingException e) {
			log.error(this.getClassNameAndMethod() + " error -> " + e.getMessage(), e);
		} 
        return null;
    }

	/**
	 * 获得一个PostMethod
	 * @param postUrl
	 * @param params
	 * @return
	 */
    @SuppressWarnings("rawtypes")
	private PostMethod newPostMethod(String postUrl, Map params) {
        PostMethod postMethod =  new PostMethod(postUrl);
        postMethod.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
        postMethod.addParameter("Content-Type", "application/x-www/form-urlencode; charset=UTF-8");
        addParamsToPostMethod(params, postMethod);
        return postMethod;
    }

    /**
     * FORM形式POST请求时,将请求参数放入PostMethod中
     * @param params
     * @param postMethod
     */
    @SuppressWarnings("rawtypes")
	private void addParamsToPostMethod(Map params, PostMethod postMethod) {
        //Groovy代码
        /*params?.each { String key, value ->
            if (value == null) return

            if (value instanceof String) {
                postMethod.addParameter(key, value)
            } else if (value instanceof Date) {
                postMethod.addParameter(key, value?.format('yyyy-MM-dd HH:mm:ss'))
            } else {
                postMethod.addParameter(key, value.toString())
            }
        }
*/
        for(Iterator it = params.keySet().iterator(); it.hasNext();) {
            String key = it.next().toString();
            Object value = params.get(key);

            if (null == value) return;

            if (value instanceof String) {
                postMethod.addParameter(key, value.toString());
            } else if (value instanceof Date) {
                postMethod.addParameter(key, dateFormat.format(value));
            } else {
                postMethod.addParameter(key, value.toString());
            }
        }
    }

    /**
     * FORM形式实现HTTP GET请求
     * @param url
     * @param params
     * @return
     */
    @SuppressWarnings("rawtypes")
	public ResponseModel get(String url, Map params) {
        GetMethod getMethod = newGetMethod(url, params);
        return request(getMethod);
    }

    /**
     * 获得一个GetMethod
     * @param url
     * @param params
     * @return
     */
    @SuppressWarnings("rawtypes")
	private GetMethod newGetMethod(String url, Map params) {
        GetMethod getMethod = new GetMethod(url);
        getMethod.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
        getMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset_utf8);
        addParamsToGetMethod(params, getMethod);
        return getMethod;
    }

    /**
     * FORM形式GET请求时,将请求参数放入GetMethod中
     * @param params
     * @param getMethod
     */
    @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
	private void addParamsToGetMethod(Map params, GetMethod getMethod) {
        List nameValuePairs = new ArrayList();

        //Groovy代码
        /*params?.each { key, value ->
            if (value == null) return

            if (value instanceof String) {
                nameValuePairs << new NameValuePair(key, value)
            } else if (value instanceof Date) {
                nameValuePairs << new NameValuePair(key, value?.format('yyyy-MM-dd HH:mm:ss'))
            } else {
                nameValuePairs << new NameValuePair(key, value?.toString())
            }
        }*/

        for(Iterator it = params.keySet().iterator(); it.hasNext();) {
            String key = it.next().toString();
            Object value = params.get(key);
            if (null == key) return;

            if (value instanceof String) {
                nameValuePairs.add(new NameValuePair(key, value.toString()));
            } else if (value instanceof Date) {
                nameValuePairs.add(new NameValuePair(key, ((Date) value).toLocaleString()));
            } else {
                nameValuePairs.add(new NameValuePair(key, value.toString()));
            }
        }

        if (nameValuePairs.size() > 0) {
            NameValuePair[] nameValuePairsArray = new NameValuePair[nameValuePairs.size()];
            for (int i = 0 ; i < nameValuePairs.size(); i ++) {
                nameValuePairsArray[i] = (NameValuePair)nameValuePairs.get(i);
            }
            getMethod.setQueryString(nameValuePairsArray);
        }
    }

    /**
     * HTTP请求方法体
     * @param httpMethod
     * @return
     */
    private ResponseModel request(HttpMethod httpMethod) {
        ResponseModel responseModel = new ResponseModel();
        HttpClient httpClient = this.getActiveHttpClient();

        long startTime = System.currentTimeMillis();
        try {
            int statusCode = httpClient.executeMethod(httpMethod);
            log.info(this.getClassNameAndMethod() + " success to execute httpMethod[ " + httpMethod.getName() + 
            		"], request url[" + httpMethod.getURI().toString() + "], response statusCode[" + statusCode + "]");
            responseModel.setStatusCode(statusCode);

            if (statusCode == HttpStatus.SC_OK) {
                responseModel.setSuccess(true);
            }

            responseModel.setResponseBody(IOUtils.toString(httpMethod.getResponseBodyAsStream()));
        } catch (Exception e) {
            try {
				log.error(this.getClassNameAndMethod() + "fail to execute httpMethod[" + httpMethod.getName() + 
						"], url[" + httpMethod.getURI().toString() + "] error -> " + e.getMessage() , e);
			} catch (URIException ex) {}
        } finally {
            httpMethod.releaseConnection();
            this.releaseHttpClient(httpClient);
            if (log.isDebugEnabled()) {
                log.debug("执行http请求耗时：" + (System.currentTimeMillis() - startTime));
            }
        }
        return responseModel;
    }

    /**
     * 获得一个可用的HttpClient
     * @return
     */
    private HttpClient getActiveHttpClient() {
        HttpClient httpClient = null;
        try {
            httpClient = HttpClient_Cache.removeFirst();

        } catch (Throwable e) {

            httpClient = this.newHttpClient();
            if (log.isDebugEnabled()) {
                log.debug("新建一个HTTPCLIENT实例,${httpClient?.hashCode()}");
            }
        }
        return httpClient;
    }

    /**
     * 释放HttpClient
     * @param httpClientInstance
     */
    private void releaseHttpClient(HttpClient httpClientInstance) {
        if (httpClientInstance != null) {
            HttpClient_Cache.addLast(httpClientInstance);
            if (log.isDebugEnabled()) {
                log.debug("释放一个HTTPCLIENT实例,${httpClientInstance?.hashCode()}");
            }
        }
    }

    /**
     * 实例化HttpClient
     * @return
     */
    private HttpClient newHttpClient() {
        HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
        httpClient.getParams().setBooleanParameter("http.protocol.expect-continue", false);
        httpClient.getParams().setContentCharset(charset_utf8);

        HttpConnectionManagerParams managerParams = httpClient.getHttpConnectionManager().getParams();
        // 设置连接超时时间(单位毫秒)
        managerParams.setConnectionTimeout(5000);
        // 设置读数据超时时间(单位毫秒)
        managerParams.setSoTimeout(10000);

        return httpClient;
    }
    
    /**
	 * 打印出当前的类名和方法名
	 * @return
	 */
	private String getClassNameAndMethod(){
		StackTraceElement[] stacks = new Throwable().getStackTrace();    
        StringBuilder sb=new StringBuilder();
        sb.append("[" ).append(this.getClass().getSimpleName()).append(".").append(stacks[1].getMethodName()).append("]  ");
        return sb.toString();
	}
}